package edu.colorado.karl.login;

import java.util.LinkedList;

import edu.colorado.karl.databases.UserDatabase;

/**
 * This class handles user authentication via login/password.
 * It reads from the 'user_data' table in the 'Users' database.
 * @author Addison LeMessurier
 * @version 3/2/08
 */
public class Authenticate {
	private static Authenticate ref;		// only allowable reference to this class
	private static Session session;			// stores data for one active session
	private static LinkedList<Session> sessions; // stores all active sessions
	private static boolean[] usedID;		// tells which session IDs are available
	private static UserRecord userRecord;

	/**
	 * This function compares the MD5 hash of the string
	 * entered to the correct hash stored in the 'Users' database.
	 * @param entered The password entered on the page.
	 * @param passwordMD5 The MD5 hash of the password stored in the database.
	 * @return <code>true</code> if the hash is identical, <code>false</code> otherwise
	 */
	public static boolean checkPassword(String entered, String passwordMD5) {
		String temp = "";

		temp = LoginUtil.MD5(entered);

		if (temp.compareTo(passwordMD5) == 0) {
			return true;
		}

		return false;
	}
	
	/**
	 * This function returns the next available session ID.
	 * @return the next available session ID.
	 */
	public static int createNewSessionID() {
		int i;

		// find the next available session ID
		for (i = 0; i < 10000; i++) {
			if (usedID[i] == false) {
				usedID[i] = true;
				break;
			}
		}

		return i;
	}
	
	/**
	 * This function instantiates the singleton object for this class.
	 * @return a globally shared instance of the Authenticate class.
	 */
	public static synchronized Authenticate getAuthenticate() {
		if (ref == null) {
			ref = new Authenticate();
		}

		if (usedID == null) {
			usedID = new boolean[10000];
		}

		if (session == null) {
			session = new Session();
		}

		if (userRecord == null) {
			userRecord = new UserRecord();
		}

		if (sessions == null) {
			sessions = new LinkedList<Session>();
		}

		return ref;
	}
	
	/**
	 * This function maps a session ID number to a user name.
	 * @param sessionID The session ID number.
	 * @return the user name associated with the given session.
	 */
	public static String getUsername(int sessionID) {
		Session s = sessions.get(sessionID);

		return s.getUsername();
	}
	
	/**
	 * This function returns a linked list containing every user name.
	 * @return a list of all user names.
	 */
	public static LinkedList<String> getUsers() {
		return UserDatabase.getUsers();
	}
	
	/**
	 * This function checks whether a given session ID is still valid.
	 * @param sessionID The ID number of the session to examine.
	 * @return <code>true</code> if the session is still valid.
	 */
	public static boolean isActive(int sessionID) {
		if ((sessionID < 0) || (sessionID > 9999)) {
			return false;
		}

		return usedID[sessionID];
	}
	
	/**
	 * This function checks whether a given session has admin access.
	 * @param sessionID The ID number of the session to examine.
	 * @return <code>true</code> if the session has admin access.
	 */
	public static boolean isAdmin(int sessionID) {
		Session s = null;
		int i;

		for (i = 0; i < sessions.size(); i++) {
			s = sessions.get(i);
			if (s.getID() == sessionID) {
				return s.getAdmin();
			}
		}

		return false;
	}

	/**
	 * This function checks a user's login information.
	 * If the login is valid, it creates a new session and returns the ID.
	 * @param username The name of the user to check.
	 * @param password The password of the user.
	 * @return a session ID number if the login is successful.
	 */
	public static int siteLogin(String username, String password) {
		Integer newSessionID;

		//sqlConnect(); // open a connection to the user_data mysql database
		//sqlGetRecords(username); // find the user's password hash and access
									// level
		
		userRecord = UserDatabase.getUserRecord(username);

		// now try to login
		if (checkPassword(password, userRecord.getPasswordMD5())) {
			// log the user in to a new session with a unique ID
			newSessionID = createNewSessionID();
			
			session = new Session();

			session.setID(newSessionID);
			session.setUsername(username);
			session.setAdmin((userRecord.getAccessLevel() > 1));

			try {
				// try adding the new session to the end of the list
				sessions.addLast(session);
			}

			catch (NullPointerException e) {
				// pointer error
				return 0;
			}

			return newSessionID;
		}

		return 0;
	}
	
	/**
	 * This function logs a user out of the site and frees their session ID.
	 * @param sessionID The ID number for the session to log out.
	 */
	public static void siteLogout(int sessionID) {
		Session s = null;
		int i;

		usedID[sessionID] = false;
		for (i = 0; i < sessions.size(); i++) {
			s = sessions.get(i);
			if (s.getID() == sessionID) {
				sessions.remove(i);
				//System.out.println("user is logging out...");
				break;
			}
		}
	}
		
	/**
	 * This function checks whether a given user is an administrator.
	 * @param username The user to examine.
	 * @return <code>true</code> if the user is an administrator.
	 */
	public static boolean userIsAdmin(String username) {
		return UserDatabase.userIsAdmin(username);
	}
	
	private Authenticate() {
		// empty for now
	}
}
